package com.samtech.jackson;

import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.introspect.AnnotatedField;
import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;
import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;

public class MorphedInnerJsonSerializer extends JsonSerializer<MorphedResult<?>> implements ContextualSerializer{
	SerializationConfig config;
	BeanProperty beanProperty;
	@Override
	public void serialize(MorphedResult<?> value, JsonGenerator jgen, SerializerProvider provider)
			throws IOException, JsonProcessingException {
		if(value==null){
			jgen.writeNull();
			return;
		}
		jgen.writeStartObject();
		try{
			MorphedResult<?> morphedResult=(MorphedResult<?>)value;
			Object presult = morphedResult.getPrimaryResult();
			BeanDescription beanDesc = config.introspect(config.constructType(presult.getClass()));
			List<BeanPropertyDefinition> props = beanDesc.findProperties();
			for (BeanPropertyDefinition bpropDef : props) {
				boolean couldSerialize = bpropDef.couldSerialize();
				String propName = bpropDef.getName();
				boolean permitted = morphedResult.isPermitted(propName);
				if(couldSerialize && permitted){
					if( bpropDef.hasGetter()){
						AnnotatedMethod getter = bpropDef.getGetter();
						Method annotated = getter.getAnnotated();
						Object invoke = annotated.invoke(presult, null);
						//TypeFactory typeFactory = config.getTypeFactory();
						
						Include serInclusion = config.getSerializationInclusion();
						if(invoke==null){
							if(serInclusion.equals(Include.NON_NULL)){
								
							}else{
								jgen.writeFieldName(propName);
								jgen.writeNull();
							}
						}else{
							if(invoke instanceof Collection<?>){
								if(((Collection<?>)invoke).isEmpty() && serInclusion.equals(Include.NON_EMPTY))continue;
							}
							if(invoke.getClass().isArray()){
								Object[] arr=(Object[])invoke;
								if(arr==null || arr.length==0){
									if(serInclusion.equals(Include.NON_EMPTY))continue;
								}
							}
							jgen.writeFieldName(propName);
							JavaType serializationType=config.constructType(invoke.getClass());
							JsonSerializer<Object> findValueSerializer = provider.findValueSerializer(serializationType, null);
							findValueSerializer.serialize(invoke, jgen, provider);
						}
					}else{//field
						AnnotatedField field = bpropDef.getField();
						Field fld = field.getAnnotated();
						Object propVal = fld.get(value);
						jgen.writeFieldName(propName);
						if(propVal==null){
							jgen.writeNull();
						}else{
							JavaType serializationType=config.constructType(propVal.getClass());
							JsonSerializer<Object> findValueSerializer = provider.findValueSerializer(serializationType, null);
							findValueSerializer.serialize(propVal, jgen, provider);
						}
					}
					
				}
			}
			Map<String, ?> expansionData = morphedResult.getExpansionData();
			if(expansionData!=null && !expansionData.isEmpty()){
				Set<String> keys = expansionData.keySet();
				for (String key : keys) {
					Object exval = expansionData.get(key);
					if(exval!=null){
						jgen.writeFieldName(key);
						JavaType serializationType=config.constructType(exval.getClass());
						JsonSerializer<Object> findValueSerializer = provider.findValueSerializer(serializationType, null);
						findValueSerializer.serialize(exval, jgen, provider);
					}
				}
			}
		} catch (IllegalAccessException e) {
			
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			
			e.printStackTrace();
		}finally{
			
		}
		jgen.writeEndObject();
	}

	@Override
	public MorphedInnerJsonSerializer createContextual(SerializerProvider provider, BeanProperty property)
			throws JsonMappingException {
		this.config = provider.getConfig();
		//this.config.c
		this.beanProperty=property;
		return this;
	}
	
}
